﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using QY.ThreadPool;
using System.Diagnostics;
using DownloadBook.Entity;

namespace DownloadBook {
    class DownloadContent {
        private readonly object m_Lock = new object();
        private readonly List<Content> originContentList;
        private readonly string directory;
        private readonly string originUrl;
        private readonly UpdateMessage updateMessage;
        private readonly int originCount;
        private int completeCount;
        private readonly Action m_Callback;
        private List<Content> contentList;
        private readonly int m_MaxThreadCount;
        private bool needStop, stoping;
        private readonly List<string> fileList = new List<string>();
        private readonly ProxyConfig proxyConfig;

        public DownloadContent(ProxyConfig proxyConfig, string directory, string originUrl, List<Content> contentList, int maxThreadCount, UpdateMessage updateMessage, Action callback) {
            this.proxyConfig = proxyConfig;
            this.directory = directory;
            this.originUrl = originUrl;
            this.updateMessage = updateMessage;
            this.originContentList = new List<Content>(contentList);
            this.m_MaxThreadCount = maxThreadCount;
            originCount = this.originContentList.Count;
            this.m_Callback = callback;

            if (!Directory.Exists(this.directory)) {
                Directory.CreateDirectory(this.directory);
            }

            //缓存文件列表
            fileList.AddRange(Directory.GetFiles(this.directory));
        }


        public delegate void UpdateMessage(string msg);
        private int workThreadCount = 0;
        public void Start() {
            lock (this.m_Lock) {
                completeCount = 0;
            }
            this.DoStart();
        }
        public void Stop() {
            needStop = true;
        }
        private void ReStart() {
            this.DoStart();
        }
        private void DoStart() {
            lock (this.m_Lock) {
                if (contentList == null) {
                    contentList = new List<Content>(originContentList);
                } else {
                    contentList.Clear();
                    contentList.AddRange(originContentList);
                }
            }
            QYThreadPool threadPool = QYThreadPoolManager.defaultPool;
            lock (this.m_Lock) {
                for (int i = 0; i < m_MaxThreadCount && i < originContentList.Count; i++) {
                    this.workThreadCount++;
                    ThreadPool.QueueUserWorkItem((obj) => {
                        Work();
                    });
                }
            }
        }

        private void Work() {
            for (; IsNeedWork() ; ) {
                //提取 Content
                Content content = this.ExtractContent();

                //判定是否已经没有 content 任务可以做了
                if (content == null) {
                    this.OnThreadTaskCompleted();
                    break;
                }

                //如果有任务需要做，那么执行下载
                this.DoDownload(content);
            }
        }

        /// <summary>
        /// 提取 Contnet
        /// </summary>
        /// <returns></returns>
        protected Content ExtractContent(){
            Content content = null;
            lock (this.m_Lock) {
                lock (contentList) {
                    if(contentList.Count > 0) {
                        content = contentList[0];
                        contentList.RemoveAt(0);
                    }
                }
            }
            return content;
        }

        /// <summary>
        /// 判定是否还要继续执行工作
        /// </summary>
        /// <returns></returns>
        protected bool IsNeedWork(){
            bool result = true;
            if (needStop) {//如果要求停止下载
                if (!stoping) {
                    stoping = true;

                    string msg = "用户要求停止下载";
                    //Console.WriteLine(msg);
                    updateMessage(msg);
                    m_Callback?.Invoke();
                }

                //标记为不需要继续了
                result = false;
            }
            return result;
        }

        /// <summary>
        /// 一个线程任务完成事件
        /// </summary>
        protected void OnThreadTaskCompleted(){
            bool lastThreadComplete = false;
            lock (this.m_Lock) {
                if(--this.workThreadCount == 0) {
                    lastThreadComplete = true;
                }
            }

            if (lastThreadComplete) {
                try {
                    Content contentNotDownload = originContentList.Find( x => x.downloadComplete == false);
                    if (contentNotDownload != null) {//如果有文件还未下载下来
                        this.ReStart();//重新开始
                    } else {
                        string msg = "下载任务已经结束";
                        //Console.WriteLine(msg);
                        updateMessage(msg);
                        m_Callback?.Invoke();
                    }
                }catch(Exception) {
                    this.ReStart();
                }
            }

        }

        /// <summary>
        /// 执行下载
        /// </summary>
        /// <param name="content"></param>
        protected void DoDownload(Content content){
            string url = content.url;


            if ((!content.downloadComplete && !content.downloading)) {//如果还未完成、也不是在下载中的，才有必要继续
                string msg;
                string filePath = Path.Combine(directory, content.GetFileName());
                string item = fileList.Find(x => x.Equals(filePath, StringComparison.OrdinalIgnoreCase));

                if (item != null || File.Exists(filePath)) {//如果文件已经存在了
                    lock (this.m_Lock) {
                        ++completeCount;
                    }
                    content.downloadComplete = true;
                    lock (this.m_Lock) {
                        double progress = Math.Floor(this.GetProgress() * 10000) / 100;
                        msg = string.Format("{0}/{1} 已完成：{2}% 忽略已经存在的章节文件：{3}", completeCount, originCount, progress, content.title);
                        updateMessage(msg);
                    }
                } else {//如果文件还未存在
                    //标记为开始下载
                    content.downloading = true;

                    //更新显示信息
                    lock (this.m_Lock) {
                        double progress = Math.Floor(this.GetProgress() * 10000) / 100;
                        msg = string.Format("{0}/{1} 已完成：{2}% 下载中：{3}", completeCount, originCount, progress, content.title);
                        updateMessage(msg);
                    }

                    string html = HttpUtil.HttpGet(url, originUrl, this.proxyConfig);
                    if (!string.IsNullOrEmpty(html)) {
                        if (!Directory.Exists(directory)) {
                            Directory.CreateDirectory(directory);
                        }

                        byte[] buffer = Encoding.Default.GetBytes(html);
                        bool success = false;
                        lock (content) {
                            try {
                                File.WriteAllBytes(filePath, buffer);
                                success = true;
                            } catch (Exception e) {
                                Console.WriteLine("{0}\n{1}", e.Message, e.StackTrace);
                            }
                        }

                        if (success) {
                            content.downloadComplete = true;
                            fileList.Add(filePath);

                            lock (this.m_Lock) {
                                ++completeCount;
                            }
                        }
                    }

                    content.downloading = false;
                }
            }
        }

        /// <summary>
        /// 获取进度
        /// </summary>
        /// <returns></returns>
        private float GetProgress(){
            float result = 0;
            int successCount = 0;
            lock(this.m_Lock){
                if(this.originContentList.Count > 0){
                    for(int i = 0; i < this.originContentList.Count; i++){
                        if(this.originContentList[i].downloadComplete){
                            successCount++;
                        }
                    }
                    result = (float)(successCount / (double)this.originContentList.Count);
                }
            }
            return result;
        }
    }
}
